#!/usr/bin/env python
#coding=utf-8

import os
import string

from gui.categoryView import CategoryView
from gui.preferencesView import PreferencesView
from interactors import CategoryInteractor
from interactors import PreferencesInteractor
from presenter.preferencesPresenter import PreferencesPresenter

from export import CSVwriter

class CategoryPresenter(object):
    ''' This is the object that takes care of the logic of the category tab.'''

    def __init__(self, model, view, interactor):
        ''' Give me all gui componentŖs. '''

        self.categories = model.get_categories()
        self.model = model
        self.view = view
        interactor.Install(self, view)
        self.isListening = True
        self.order = -1 # sort ascending
        self.initView()
        view.start()

    def initView(self):
        '''
        Upon first start, load the categories, set the selection on the first
        category and update the view.
        '''
        self.view.setCategories(self.categories)
        self.view.setSelectedCategory(0)
        self.loadViewFromModel()

    def loadViewFromModel(self):
        ''' Load data from model.
        1. guard against recursive call caused by events generated uppon
           loading of the data
        2. update the view data with the information from the model
        '''

        if self.isListening:
            self.isListening = False
            self.refreshCategoryList()
            self.view.setCategory(self.selectedCategory)
            self.updateWindowTitle()
            self.enableSaveAndCancel(False)
            self.isListening = True
            if self.order is -1:
                self.view.set_order_label("A->Z")
            else:
                self.view.set_order_label("Z->A")

    def refreshCategoryList(self):
        '''
        1. save the selection
        2. update the list
        3. restore the selection
        '''

        currentCategory = self.view.getSelectedCategory()
        self.categories = self.model.get_categories()
        if self.order is 1:
            self.categories.reverse()

        self.view.setCategories(self.categories)
        self.view.setSelectedCategory(currentCategory)
        self.selectedCategory = self.view.getSelectedCategoryValue()

    def enableSaveAndCancel(self, enabled):
#TODO pydoc update enable
        self.view.setSaveEnabled(enabled)
        self.view.setCancelEnabled(enabled)

    def updateModel(self):
        ''' Update database and refresh view. '''

        self.view.getSelectedCategory()
        self.view.getSelectedCategoryValue()
        self.model.update_category(self.selectedCategory,
                                    self.view.getCategory())

        self.loadViewFromModel()

    def updateWindowTitle(self):
        self.view.setWindowTitle("Edit category: " + self.view.getCategory())

    def dataFieldUpdated(self):
            '''
            Upon a change in the view, enables the Apply/Cancel buttons.
            Since the Category might have been changed we also update the title
            of the frame
            Optimisation: If the data is updated by the loadViewFromModel
            ignore the call
            '''
            if self.isListening:
                self.enableSaveAndCancel(True)
                self.updateWindowTitle()

    def add_new_category(self):
        ''' '''

        category = self.view.display_dialog_box()
        self.model.save_category(category)
        self.loadViewFromModel()

    def change_sort_order(self):
        ''' Change label of sort btn and change list sort order. '''

        self.order = -1 * self.order
        self.loadViewFromModel()

class MainPresenter(object):
    ''' This is the object that takes care of the applicationn logic. '''

    def __init__(self, model, view, interactor):
        ''' Give me all gui components. '''

        self.model = model
        self.headline = (self.model.config.get('Settings','lang1'),
                         self.model.config.get('Settings','lang2'),
                        'Categories')
        self.model.create_database()
        self.view = view
        self.view.show_splash_screen()
        self.view.create(model.config)
        self.set_layout()
        self.populate_edit_panel()
        interactor.Install(self, view)

        self.categories = model.get_categories()
        self.isListening = True
        self.init_view()
        view.start()


    def create_set(self, word):
        ''' Create a set out of strings containing commas. '''

        word = word.split(',')
        return set(map(string.strip, word))

    def export_vocs(self):
        ''' Export the vocs in csv file format.

        If we do not want to break the MVP pattern we have to call the
        view and supply the data and export function.
        '''

        vocs = self.model.get_vocabularies()
        headline = (self.model.config.get('Settings','lang1'),
                    self.model.config.get('Settings','lang2'),'Categories')
        data = []
        for voc in vocs:
            line = (voc.lang1,voc.lang2,
                    ", ".join((str(el.category) for el in voc.category)))
            data.append(line)
        dbfile = self.model.config.get('Settings','dbfile')
        filename = dbfile.split('.')[0]+'.csv'

        self.view.export_csv(CSVwriter, data, headline, filename)

    def init_view(self):
        ''' On first start fill our empty view with data.

            - load the categories
        '''

        self.view.set_categories(self.categories)
        self.view.set_selected_category(0)

    def get_notebook_page_label(self):

        return self.view.notebook.GetPageText(self.view.notebook.GetSelection())

    def clear_search(self):
        '''Clear search field and show voc list.'''

        self.view.notebook.editPanel.clear_search_field()
        self.refresh_voc_list(search = False)

    def populate_edit_panel(self):
        ''' Populate the edit panel with all vocs.'''


        self.view.notebook.editPanel.set_headline(self.headline)
        self.refresh_voc_list(search = False)

    def print_vocs(self):
        ''' Print filtered vocs.  '''

        data = []
        vocs = self.model.get_vocabularies()
        searchString = self.view.notebook.editPanel.get_search_input()
        for voc in vocs:
            categories = ", ".join((str(el.category)
                                    for el in voc.category))
            if searchString in voc.lang1 or searchString in voc.lang2:
                data.append((voc.lang1, voc.lang2, categories))

        self.view.notebook.editPanel.print_vocs(self.headline, data)

    def quit(self):
        ''' Destroy the application window. '''

        self.view.quit()

    def refresh_categories(self, msg=''):
        ''' Refresh categories in the input and editPanel.

        The msg param is used by the pubsub.Publisher()
        '''

        categories = self.model.get_categories()
        categories = [category.category for category in categories]

        self.view.notebook.inputPanel.set_categories(categories)
        categories.insert(0,'All')
        self.view.notebook.learnPanel.set_choicelst(categories)

    def refresh_voc_list(self, search=True):
        ''' Select vocs depending on the search criteria. '''

        data = {}
        vocs = self.model.get_vocabularies()
        if search:
            searchString = self.view.notebook.editPanel.get_search_input()
            self.view.notebook.editPanel.clear_voc_lst()
            for voc in vocs:
                categories = ", ".join((str(el.category)
                                        for el in voc.category))
                if searchString in voc.lang1 or searchString in voc.lang2:
                    data[voc.id] = (voc.lang1, voc.lang2, categories)
        else:
            for voc in vocs:
                categories = ", ".join((str(el.category)
                                        for el in voc.category))
                data[voc.id] = (voc.lang1, voc.lang2, categories)
        self.view.notebook.editPanel.populate_list(data)

    def refresh_input_tab(self):
        ''' Refresh only the input tab. '''

        categories = self.model.get_categories()
        categories = [category.category for category in categories]
        self.view.notebook.inputPanel.clear_lang1_input()
        defaultCategory = self.model.config.get('Settings', 'defaultCategory')
        self.view.notebook.inputPanel.clear_lang2_input()
#        self.view.notebook.inputPanel.deselect_categories()
        self.view.notebook.inputPanel.set_categories(categories)
        self.view.notebook.inputPanel.select_category_lst_item(defaultCategory)
        self.view.notebook.inputPanel.set_submit_btn_label('Save')
        self.view.notebook.inputPanel.set_yesBtn(True)
        self.view.notebook.inputPanel.set_focus()

    def refresh_view(self, msg=''):
        ''' Refresh all variable text fields.

        msg is used by the Publisher()
         '''

        self.view.show_message('Refresh')

    def show_context_menu(self):
        self.view.notebook.editPanel.show_context_menu()

    def set_focus_in_edit_panel(self):
        '''Set the focus on the search field in the edit panel.'''

        self.view.notebook.editPanel.set_focus()

    def update_input_panel_wiht_voc(self):
        ''' Update the inputPanel with voc from editPanel. '''

        self.updateVoc = self.model.get_voc_by_id(self.vocId)
        self.view.notebook.inputPanel.set_lang1_input(self.updateVoc.lang1)
        self.view.notebook.inputPanel.set_lang2_input(self.updateVoc.lang2)
        self.view.notebook.inputPanel.set_noBtn(True)
        self.view.notebook.inputPanel.deselect_categories()
        for obj in self.updateVoc.category:
            self.view.notebook.inputPanel.select_category_lst_item(
                                                                obj.category)
        self.view.notebook.inputPanel.set_submit_btn_label('Update')
        self.view.notebook.SetSelection(2)

    def save_or_update_voc(self):
        ''' Save or update voc in database.

            Depending on the button label "Update" or "Save" we are updating
            an old voc or storing a new one.
         '''

        label= self.view.notebook.inputPanel.get_submit_btn_label()

        selection = []
        readyToSave = True
        categories = self.view.notebook.inputPanel.get_selected_categories()
        lang1 = self.view.notebook.inputPanel.get_lang1_input()
        if not lang1:
            readyToSave = False
            self.view.show_message('Please enter a %s text'\
                (self.model.config.get('Settings','lang1')))

        lang2 = self.view.notebook.inputPanel.get_lang2_input()
        if not lang2:
            readyToSave = False
            self.view.show_message('Please enter a %s text'\
                            (self.model.config.get('Settings','lang2')))

        if not categories:
            self.view.show_message('You have to select at least one category.')
            readyToSave = False

        yesBtn = self.view.notebook.inputPanel.get_yesBtn()

        if readyToSave:
            if label == 'Save':
                self.model.save_new_voc(lang1, lang2, categories, yesBtn)
                self.view.show_message('Saved new vocable')
            else:
                self.model.update_voc(
                        self.updateVoc, lang1, lang2, categories, yesBtn)
            self.refresh_input_tab()
            self.refresh_voc_list(search=True)

    def set_layout(self):
        ''' Sets some gui layouts. '''

        background = self.model.config.get('Colours', 'background')
        self.view.set_backgroundcolour(background)
        self.view.notebook.inputPanel.set_backgroundcolour(background)
        self.view.notebook.editPanel.set_backgroundcolour(background)
        self.view.notebook.learnPanel.set_backgroundcolour(background)
        self.view.notebook.learnPanel.bottomPanel.SetBackgroundColour(
            background)

    def set_selected_item(self, key):
        ''' Remember the selected vocId for further use. '''

        self.vocId = self.view.notebook.editPanel.get_selected_vocId(key)

    def show_about_box(self):
        ''' About Menu was clicked. Show the About dialog box. '''

        self.view.show_about_box(self.model.config.get('Settings','version'))

    def show_category_window(self):
        ''' Run the category window in its own MVP logic. '''

        CategoryPresenter(self.model, CategoryView(), CategoryInteractor())

    def show_preferences_window(self):
        ''' Open a new frame for editing preferences. '''

        PreferencesPresenter(self.model, PreferencesView(self.model),
            PreferencesInteractor())

    def speak(self):
        #cmd = '/usr/bin/espeak -v%s+12 "%s"' %(self.abbreviation,
        #    self.view.notebook.learnPanel.get_question())
        voicePrg = self.model.config.get('Settings','voiceProgram')
        word = self.view.notebook.learnPanel.get_question()
        if voicePrg == 'festival':
            festivalPath = self.model.config.get('Settings','festivalpath')
            cmd = '/bin/echo "%s"|%s --tts'\
                % (word,
                   festivalPath)
        elif voicePrg == 'espeak':
            espeakPath = self.model.config.get('Settings','espeakpath')
            cmd = '%s -v%s "%s"' % (espeakPath, self.abbreviation, word)
        os.system(cmd)

    def toggle_learning(self):
        ''' Depending on the start button state we start or stop a session. '''
        #TODO: eval number of vocs in category and hint if no voc available

        value = self.view.notebook.learnPanel.get_start_btn_label()
        if value == 'Start':
            selectedCategory = self.view.notebook.learnPanel.get_category()
            if selectedCategory=='All':
               vocCount = self.model.count_voc()
            else:
                vocCount = self.model.count_voc_by_category(selectedCategory)
            if vocCount == 0:
                self.view.show_message('Category %s contains no vocs.' %selectedCategory)
            else:
                self.start_learning()
        else:
            self.stop_learning()

    def hide_filter(self):
        ''' Hide the filter elements from the learnPanel. '''

        self.view.notebook.learnPanel.hide_directionBox()
        self.view.notebook.learnPanel.hide_setBox()
        self.view.notebook.learnPanel.hide_catCtrl()
        self.view.notebook.learnPanel.hide_categoryChoice()
        self.view.notebook.learnPanel.hide_cb_effectless()
        self.view.notebook.learnPanel.hide_cb_oldest()

    def show_filter(self):
        ''' Hide the filter elements from the learnPanel. '''

        self.view.notebook.learnPanel.enable_directionBox()
        self.view.notebook.learnPanel.enable_setBox()
        self.view.notebook.learnPanel.enable_catCtrl()
        self.view.notebook.learnPanel.enable_categoryChoice()
        self.view.notebook.learnPanel.enable_cb_effectless()
        self.view.notebook.learnPanel.enable_cb_oldest()


    def start_learning(self):
        ''' Do some preparations and start loop. '''

        self.view.notebook.learnPanel.set_start_btn_label('Stop')
        self.view.notebook.learnPanel.show_bottom_panel()
        self.hide_filter()

        self.statistic = {
            'total':0,
            'asked': 0,
            'correct': 0,
            'wrong': 0}

        direction=self.view.notebook.learnPanel.get_direction_box()
        if direction.startswith(self.model.config.get('Settings','lang1')):
            self.direction = 'lang1'
            self.abbreviation = self.model.config.get('Settings','lang1abbr')
            self.view.update_learn_panel('lang1',self.model.config)
        elif direction.startswith(self.model.config.get('Settings','lang2')):
            self.direction = 'lang2'
            self.abbreviation = self.model.config.get('Settings','lang2abbr')
            self.view.update_learn_panel('lang2',self.model.config)
        self.quantum=self.view.notebook.learnPanel.get_set_box()


        self.noneEffective = self.view.notebook.learnPanel.get_cb_effectless()
        self.categoryFilter = self.view.notebook.learnPanel.get_category()
        self.oldestFirst = self.view.notebook.learnPanel.get_cb_oldestFirst()

        if self.categoryFilter != 'All':
            cat = self.categoryFilter
            self.statistic['total'] = str(self.model.count_voc_by_category(cat))
        elif self.quantum == 'All':
            self.statistic['total'] = str(self.model.count_voc())
        else:
            self.statistic['total'] = str(self.model.count_error_voc(self.direction))


        self.learn()

    def learn(self):
        lang1=self.model.config.get('Settings','lang1')
        lang2=self.model.config.get('Settings','lang2')

        self.view.update_statusBar(**self.statistic)
        if self.quantum == 'All':
            if self.oldestFirst:
                self.testobj = self.model.get_oldest_voc()
            else:
                self.testobj = self.model.get_random_voc(
                                            category=self.categoryFilter)
        else:
            self.testobj = self.model.get_random_error_voc(
                self.direction, category=self.categoryFilter)

        self.view.notebook.learnPanel.update_question_input(
                                        getattr(self.testobj, self.direction))
        voc_categories = [str(el.category) for el in self.testobj.category]
        tooltip = ", ".join(voc_categories)
        tooltip = 'Categories: ' + tooltip
        self.view.notebook.learnPanel.update_voc_categories(voc_categories)
        self.view.notebook.learnPanel.update_question_input_tooltip(tooltip)
        self.view.notebook.learnPanel.update_answer_input('')
        self.view.notebook.learnPanel.set_focus_on_answer()

        if self.direction == 'lang1':
            errorState = self.testobj.err_lang1
        else:
            errorState = self.testobj.err_lang2
        self.view.notebook.learnPanel.update_state_input(str(errorState))
        if errorState < 0:
            self.view.notebook.learnPanel.set_state_colour('red')
        else:
            self.view.notebook.learnPanel.set_state_colour('black')

    def verify_voc(self):
        ''' Verify the entered vocabulary. '''

        enteredWord = self.view.notebook.learnPanel.get_answer_input()
        if ',' in enteredWord:
            enteredWord = self.create_set(enteredWord)

        if self.direction=='lang1':
            compareWith = 'lang2'
            lang = 'lang1'
        else:
            compareWith = 'lang1'
            lang = 'lang2'

        compareWord = getattr(self.testobj,compareWith)
        if ',' in compareWord:
            compareWord = self.create_set(compareWord)
#TODO refactore method

        if enteredWord == compareWord:
            self.view.show_message('Well done.')
            self.statistic['correct'] += 1
            if self.noneEffective is True:
                pass # do nothing
            else:
                self.update_error_table(lang,match=True)
        else:
            msg = 'Ups\nThe right word would be: %s' \
                   %getattr(self.testobj,compareWith)
            self.view.show_message(msg)
            self.statistic['wrong'] += 1
            if self.noneEffective is True:
                pass # do nothing
            else:
                self.update_error_table(lang,match=False)

        if self.noneEffective:
            pass
        else:
            if self.categoryFilter != 'All':
                cat = self.categoryFilter
                self.statistic['total'] =\
                    str(self.model.count_voc_by_category(cat))
            elif self.quantum == 'All':
                self.statistic['total'] = str(self.model.count_voc())
            else:
                self.statistic['total'] = str(self.model.count_error_voc(
                                                            self.direction))
        self.statistic['asked'] += 1
        self.view.update_statusBar(**self.statistic)
        self.learn()

    def update_error_table(self, lang = None, match = False):
        """ lang1 = 1 => set lang1 row else lang2 raw """

        offset = self.model.config.get('Settings','errorOffset')

        if match:
            self.model.update_voc_error(self.testobj, lang, 1)
        else:
            self.model.update_voc_error(self.testobj, lang, -1)

    def stop_learning(self):
        ''' Hide gui elements. '''

        self.view.notebook.learnPanel.set_start_btn_label('Start')
        self.show_filter()
        self.view.notebook.learnPanel.hide_bottom_panel()

    def show_message(self, txt):
        self.view.show_message(txt)


    def ask_user(self):
        voc = self.model.get_voc_by_id(self.vocId)
        txt = "%s\n%s" % (voc.lang1, voc.lang2)
        result = self.view.notebook.editPanel.ask_user('Delete voc: \n%s' % txt)
        if result:
            self.model.delete_voc_by_id(self.vocId)
            self.refresh_voc_list(search = True)
